/*
** Copyright (C) 2010 Calin Radoni
**
** This file is part of CHScanner.
**
** CHScanner is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** CHScanner is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with CHScanner. If not, see <http://www.gnu.org/licenses/>.
*/

#include "StdAfx.h"
#include "PacketBuilder6.h"

//---------------------------------------------------------------------------

PacketBuilder6::PacketBuilder6(void)
{
	packet   = NULL;
	packSize = 0;

	memset(&pseudoHeader, 0, sizeof(IPv6PseudoHeader));

	memset(routerMulticastMAC, 0, 6);
	routerMulticastMAC[0] = 0x33;
	routerMulticastMAC[1] = 0x33;
	routerMulticastMAC[5] = 0x02;

	memset(unspecifiedAddress.u.Byte, 0, 16);

	memset(allRouterMulticastAddress.u.Byte, 0, 16);
	allRouterMulticastAddress.u.Byte[0]  = 0xFF;
	allRouterMulticastAddress.u.Byte[1]  = 0x02;
	allRouterMulticastAddress.u.Byte[15] = 0x02;
}

PacketBuilder6::~PacketBuilder6(void)
{
	if(packet!=NULL){
		delete[] packet;
		packet=NULL;
	}
	packSize = 0;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::AddEthernetHeaderForIPMulticast(BYTE srcMACIn[6], in_addr6* addrIn)
{
	// coded according to RFC 2464
	BYTE dstMAC[6];

	if(addrIn==NULL) return false;

	dstMAC[0] = 0x33;
	dstMAC[1] = 0x33;
	dstMAC[2] = addrIn->u.Byte[12];
	dstMAC[3] = addrIn->u.Byte[13];
	dstMAC[4] = addrIn->u.Byte[14];
	dstMAC[5] = addrIn->u.Byte[15];

	return AddEthernetHeader(srcMACIn, dstMAC);
}

bool PacketBuilder6::AddEthernetHeaderRouterMulticast(BYTE srcMACIn[6])
{
	// router multicast MAC is 33:33:00:00:00:02
	// which is the derived from IPv6's AllRoutersLinkLocal (FF01::02) or IPv6's AllRoutersNodeLocal (FF02::02)
	// and AddEthernetHeaderForIPMulticast(...)

	return AddEthernetHeader(srcMACIn, routerMulticastMAC);
}

bool PacketBuilder6::AddEthernetHeaderForIPNS(BYTE srcMACIn[6], in_addr6* addrIn)
{
	// send a NS for a "quite unknown" IPv6 address needs the usage of the corresponding
	// multicast address (see HostMulticastFromIP) and AddEthernetHeaderForIPMulticast(...)

	BYTE dstMAC[6];

	if(addrIn==NULL) return false;

	dstMAC[0] = 0x33;
	dstMAC[1] = 0x33;
	dstMAC[2] = 0xFF; // this is NOT addrIn->u.Byte[12] because, by changing the address to multicast, this becomes 0xFF !
	dstMAC[3] = addrIn->u.Byte[13];
	dstMAC[4] = addrIn->u.Byte[14];
	dstMAC[5] = addrIn->u.Byte[15];

	return AddEthernetHeader(srcMACIn, dstMAC);
}

bool PacketBuilder6::AddEthernetHeader(BYTE srcMACIn[6], BYTE dstMACIn[6])
{
	EthernetHeaderPG* ehpg;
	BYTE* buff;
	int size;

	size = packSize + 14;

	try{
		buff = new BYTE[size];
	}
	catch(...){
		buff = NULL;
	}
	if(buff==NULL) return false;

	memcpy(&buff[0], &dstMACIn[0], 6);
	memcpy(&buff[6], &srcMACIn[0], 6);

	ehpg = (EthernetHeaderPG*)buff;
	ehpg->lenType = htons(0x86dd);

	memcpy(&buff[14], packet, packSize);

	delete[] packet;
	packet   = buff;
	packSize = size;

	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::GetIPv6MulticastAddress(MultiCastAddrType mcat, in_addr6* addrIn)
{
	int i;

	if(addrIn==NULL) return true;

	for(i=0; i<8; i++)
		addrIn->u.Word[i] = 0x0000;

	switch(mcat){
		case __mcatAllNodes_NodeLocal :
			addrIn->u.Byte[0]  = 0xFF;
			addrIn->u.Byte[1]  = 0x01;
			addrIn->u.Byte[15] = 0x01;
			break;
		case __mcatAllNodes_LinkLocal :
			addrIn->u.Byte[0]  = 0xFF;
			addrIn->u.Byte[1]  = 0x02;
			addrIn->u.Byte[15] = 0x01;
			break;

		case __mcatAllRouters_NodeLocal :
			addrIn->u.Byte[0]  = 0xFF;
			addrIn->u.Byte[1]  = 0x01;
			addrIn->u.Byte[15] = 0x02;
			break;
		case __mcatAllRouters_LinkLocal :
			addrIn->u.Byte[0]  = 0xFF;
			addrIn->u.Byte[1]  = 0x02;
			addrIn->u.Byte[15] = 0x02;
			break;
		case __mcatAllRouters_SiteLocal :
			addrIn->u.Byte[0]  = 0xFF;
			addrIn->u.Byte[1]  = 0x05;
			addrIn->u.Byte[15] = 0x02;
			break;

		default:
			return false;
	}

	return true;
}

bool PacketBuilder6::IPv6AddressIsAllNodesLinkLocal(in_addr6* iaIn)
{
	if(iaIn==NULL) return false;

	if(iaIn->u.Byte[0]  != 0xFF) return false;
	if(iaIn->u.Byte[1]  != 0x02) return false;
	if(iaIn->u.Byte[2]  != 0x00) return false;
	if(iaIn->u.Byte[3]  != 0x00) return false;
	if(iaIn->u.Byte[4]  != 0x00) return false;
	if(iaIn->u.Byte[5]  != 0x00) return false;
	if(iaIn->u.Byte[6]  != 0x00) return false;
	if(iaIn->u.Byte[7]  != 0x00) return false;
	if(iaIn->u.Byte[8]  != 0x00) return false;
	if(iaIn->u.Byte[9]  != 0x00) return false;
	if(iaIn->u.Byte[10] != 0x00) return false;
	if(iaIn->u.Byte[11] != 0x00) return false;
	if(iaIn->u.Byte[12] != 0x00) return false;
	if(iaIn->u.Byte[13] != 0x00) return false;
	if(iaIn->u.Byte[14] != 0x00) return false;
	if(iaIn->u.Byte[15] != 0x01) return false;

	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildIPHeader(
							BYTE* destIn,
							in_addr6* srcAddr, in_addr6* dstAddr,
							BYTE bTrafficClass, DWORD dwFlowLabel,
							WORD wPayloadLen, BYTE bNextHeader,
							BYTE bHopLimit)
{
IP6HeaderPG* iph;
DWORD dwV, dwT, dwF;

	if(destIn==NULL)  return false;
	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;

	iph = (IP6HeaderPG*)destIn;

	dwV = 0x60000000;

	dwT = (DWORD)bTrafficClass;
	dwT = dwT<<20;
	dwT &= 0x0FF00000;

	dwF = dwFlowLabel & 0x000FFFFF;

	iph->VTF = htonl(dwV | dwT | dwF);

	iph->PayloadLen = htons(wPayloadLen);
	iph->NextHeader = bNextHeader;
	iph->HopLimit   = bHopLimit;

	memcpy(iph->Source,      srcAddr->u.Byte, 16);
	memcpy(iph->Destination, dstAddr->u.Byte, 16);

	return true;
}

//---------------------------------------------------------------------------

USHORT PacketBuilder6::ComputeCheckSum(USHORT *buffer, int size)
{
unsigned long cksum;
USHORT* dataIn;
int i, cnt;

	cksum = 0;


	dataIn = (USHORT*)&pseudoHeader;
	cnt    = sizeof(IPv6PseudoHeader) / sizeof(USHORT);
	for(i=0; i<cnt; i++)
		cksum += dataIn[i];

	dataIn = buffer;
	cnt    = size;
	i      = 0;
	while(cnt>1){
		cksum += dataIn[i];
		cnt   -= sizeof(USHORT);
		i++;
	}

	if(cnt>0){
		cksum += *(UCHAR*)dataIn[i];
	}

	cksum = (cksum >> 16) + (cksum & 0xffff);
	cksum += (cksum >>16);
	return (USHORT)(~cksum);
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketICMPEcho(
			in_addr6* srcAddr, in_addr6* dstAddr,
			BYTE bTrafficClass, DWORD dwFlowLabel,
			BYTE bHopLimit,
				
			bool request,				///< request or reply ?
			WORD wId,					///< ICMPv6 Id
			WORD wSeq,					///< ICMPv6 Sequence

			BYTE* bpData,				///< data
			DWORD dwDataLen				///< length of data
		)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;
	if(bpData==NULL && dwDataLen!=0) return false;

	nextHeader = 0x3A;	// ICMPv6

	ttsize  = sizeof(IP6HeaderPG);
	ttsize += sizeof(ICMP6EchoPG);
	ttsize += dwDataLen;

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}
	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen  = sizeof(ICMP6EchoPG);
	dwLen += dwDataLen;

	if(!BuildIPHeader(packet, srcAddr, dstAddr, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

ICMP6EchoPG* icmph;

	icmph=(ICMP6EchoPG*)&(packet[sizeof(IP6HeaderPG)]);

	icmph->Type = request ? 128 : 129;
	icmph->Code = 0;
	icmph->Id   = htons(wId);
	icmph->Seq  = htons(wSeq);

BYTE* bbb;

	if(bpData!=NULL){
		bbb = &(packet[sizeof(IP6HeaderPG) + sizeof(ICMP6EchoPG)]);
		memcpy(bbb, bpData, dwDataLen);
	}

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte, 16);
	memcpy(pseudoHeader.Destination, dstAddr->u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	// for computing the checksum
	icmph->Checksum = 0;
	// then the new value
	icmph->Checksum = ComputeCheckSum((unsigned short*)icmph, dwLen);

	packSize = ttsize;
	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketICMPRouterSolicitation(
			BYTE bTrafficClass, DWORD dwFlowLabel,
			BYTE bHopLimit
		)
{
	return BuildPacketICMPRouterSolicitation(&unspecifiedAddress, bTrafficClass, dwFlowLabel, bHopLimit, NULL, 0);
}

bool PacketBuilder6::BuildPacketICMPRouterSolicitation(
			in_addr6* srcAddr,
			BYTE bTrafficClass, DWORD dwFlowLabel,
			BYTE bHopLimit,

			BYTE* options,
			DWORD optLen
		)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(options==NULL && optLen!=0) return false;

	nextHeader = 0x3A;	// ICMPv6

	ttsize = sizeof(IP6HeaderPG) + sizeof(ICMP6RouterPG) + optLen;

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}
	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen = sizeof(ICMP6RouterPG) + optLen;

	if(!BuildIPHeader(packet, srcAddr, &allRouterMulticastAddress, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

ICMP6RouterPG* icmph;

	icmph = (ICMP6RouterPG*)&(packet[sizeof(IP6HeaderPG)]);

	icmph->Type     = 133;
	icmph->Code     = 0;
	icmph->Reserved = 0;

BYTE* bbb;
DWORD idx;

	bbb = (BYTE*)&(packet[sizeof(IP6HeaderPG) + sizeof(ICMP6RouterPG)]);
	for(idx=0; idx<optLen; idx++)
		bbb[idx] = options[idx];

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte,                  16);
	memcpy(pseudoHeader.Destination, allRouterMulticastAddress.u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	// for computing the checksum
	icmph->Checksum = 0;
	// then the new value
	icmph->Checksum = ComputeCheckSum((unsigned short*)icmph, dwLen);

	packSize = ttsize;
	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::HostMulticastFromIP(in_addr6* addrIn)
{
	if(addrIn==NULL) return false;

	addrIn->u.Byte[0]  = 0xFF;
	addrIn->u.Byte[1]  = 0x02;
	addrIn->u.Byte[2]  = 0x00;
	addrIn->u.Byte[3]  = 0x00;
	addrIn->u.Byte[4]  = 0x00;
	addrIn->u.Byte[5]  = 0x00;
	addrIn->u.Byte[6]  = 0x00;
	addrIn->u.Byte[7]  = 0x00;
	addrIn->u.Byte[8]  = 0x00;
	addrIn->u.Byte[9]  = 0x00;
	addrIn->u.Byte[10] = 0x00;
	addrIn->u.Byte[11] = 0x01;
	addrIn->u.Byte[12] = 0xFF;

	// Last three octets should be unmodified !

	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketICMPAddrDetection(
			in_addr6* reqAddr,
			BYTE bTrafficClass, DWORD dwFlowLabel,
			BYTE bHopLimit
		)
{
	return BuildPacketICMPNeighborSolicitation(&unspecifiedAddress, reqAddr, bTrafficClass, dwFlowLabel, bHopLimit, NULL, 0);
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketICMPNeighborSolicitation(
			in_addr6* srcAddr,
			in_addr6* dstAddr,
			BYTE bTrafficClass, DWORD dwFlowLabel,
			BYTE bHopLimit,

			BYTE* options,
			DWORD optLen
		)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;
in_addr6 dstMulticastAddr;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;
	if(options==NULL && optLen!=0) return false;

	memcpy(dstMulticastAddr.u.Byte, dstAddr->u.Byte, 16);
	if(!HostMulticastFromIP(&dstMulticastAddr)) return false;

	nextHeader = 0x3A;	// ICMPv6

	ttsize = sizeof(IP6HeaderPG) + sizeof(ICMP6NeighborPG) + optLen;

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}

	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen = sizeof(ICMP6NeighborPG) + optLen;

	if(!BuildIPHeader(packet, srcAddr, &dstMulticastAddr, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

ICMP6NeighborPG* icmph;

	icmph = (ICMP6NeighborPG*)&(packet[sizeof(IP6HeaderPG)]);

	icmph->Type     = 135;
	icmph->Code     = 0;
	icmph->Reserved = 0;
	memcpy(icmph->TargetAddr, dstAddr->u.Byte, 16);

BYTE* bbb;
DWORD idx;

	bbb = (BYTE*)&(packet[sizeof(IP6HeaderPG) + sizeof(ICMP6NeighborPG)]);
	for(idx=0; idx<optLen; idx++)
		bbb[idx] = options[idx];

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte,         16);
	memcpy(pseudoHeader.Destination, dstMulticastAddr.u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	// for computing the checksum
	icmph->Checksum = 0;
	// then the new value
	icmph->Checksum = ComputeCheckSum((unsigned short*)icmph, dwLen);

	packSize = ttsize;
	return true;
}

bool PacketBuilder6::BuildPacketICMPNeighborAdvertisment(
			in_addr6* srcAddr,
			in_addr6* dstAddr,
			BYTE bTrafficClass, DWORD dwFlowLabel,
			BYTE bHopLimit,

			DWORD dwNaFlags,

			BYTE* options,
			DWORD optLen
		)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;

	/*
	dwNaFlags:
		0x80000000 = router
		0x40000000 = solicited
		0x20000000 = override
	Ex.:
		0x60000000 = Not router, solicited, override
	*/

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;
	if(options==NULL && optLen!=0) return false;

	nextHeader = 0x3A;	// ICMPv6

	ttsize = sizeof(IP6HeaderPG) + sizeof(ICMP6NeighborPG) + optLen;

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}

	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen = sizeof(ICMP6NeighborPG) + optLen;

	if(!BuildIPHeader(packet, srcAddr, dstAddr, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

ICMP6NeighborPG* icmph;

	icmph = (ICMP6NeighborPG*)&(packet[sizeof(IP6HeaderPG)]);

	icmph->Type     = 136;
	icmph->Code     = 0;
	icmph->Reserved = htonl(dwNaFlags);
	memcpy(icmph->TargetAddr, srcAddr->u.Byte, 16);

BYTE* bbb;
DWORD idx;

	bbb = (BYTE*)&(packet[sizeof(IP6HeaderPG) + sizeof(ICMP6NeighborPG)]);
	for(idx=0; idx<optLen; idx++)
		bbb[idx] = options[idx];

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte, 16);
	memcpy(pseudoHeader.Destination, dstAddr->u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	// for computing the checksum
	icmph->Checksum = 0;
	// then the new value
	icmph->Checksum = ComputeCheckSum((unsigned short*)icmph, dwLen);

	packSize = ttsize;
	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketICMPMLDQuery(
		in_addr6* srcAddr, in_addr6* dstAddr,
		BYTE bTrafficClass, DWORD dwFlowLabel,
		BYTE bHopLimit,

		WORD maximumResponseDelay
	)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;

	nextHeader = 0x00;	// Hop-by-Hop Options Header

	ttsize  = sizeof(IP6HeaderPG);
	ttsize += sizeof(HopByHopHeaderRouterAlertPG);
	ttsize += sizeof(ICMP6MLD);

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}
	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen = sizeof(ICMP6MLD) + sizeof(HopByHopHeaderRouterAlertPG);

	if(!BuildIPHeader(packet, srcAddr, dstAddr, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

HopByHopHeaderRouterAlertPG* hopByHop;
	hopByHop = (HopByHopHeaderRouterAlertPG*)&(packet[sizeof(IP6HeaderPG)]);
	hopByHop->NextHeader = 0x3A;	// ICMPv6
	hopByHop->HdrExtLen  = 0;
	hopByHop->Data[0]    = 0x05;	// Router Alert option
	hopByHop->Data[1]    = 0x02;
	hopByHop->Data[2]    = 0x00;
	hopByHop->Data[3]    = 0x00;
	hopByHop->Data[4]    = 0x01;	// PadN option
	hopByHop->Data[5]    = 0x00;

ICMP6MLD* mldh;

	mldh = (ICMP6MLD*)&(packet[sizeof(IP6HeaderPG) + sizeof(HopByHopHeaderRouterAlertPG)]);

	memset(mldh, 0, sizeof(ICMP6MLD));
	mldh->Type = 130;
	mldh->Code = 0;
	mldh->MaximumResponseDelay = htons(maximumResponseDelay);
	mldh->Reserved = 0;

	if(!IPv6AddressIsAllNodesLinkLocal(dstAddr))
		memcpy(mldh->McastAddr, dstAddr, sizeof(in_addr6));

	nextHeader = 0x3A;	// Hop-by-Hop Options Header
	dwLen      = sizeof(ICMP6MLD);

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte, 16);
	memcpy(pseudoHeader.Destination, dstAddr->u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	// for computing the checksum
	mldh->Checksum = 0;
	// then the new value
	mldh->Checksum = ComputeCheckSum((unsigned short*)mldh, dwLen);

	packSize = ttsize;
	return true;
}

bool PacketBuilder6::BuildPacketICMPMLDResponse(
		in_addr6* srcAddr, in_addr6* mcastAddr,
		BYTE bTrafficClass, DWORD dwFlowLabel,
		BYTE bHopLimit,

		WORD maximumResponseDelay
	)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(mcastAddr==NULL) return false;

	nextHeader = 0x00;	// Hop-by-Hop Options Header

	ttsize  = sizeof(IP6HeaderPG);
	ttsize += sizeof(HopByHopHeaderRouterAlertPG);
	ttsize += sizeof(ICMP6MLD);

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}
	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen = sizeof(ICMP6MLD) + sizeof(HopByHopHeaderRouterAlertPG);

	if(!BuildIPHeader(packet, srcAddr, mcastAddr, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

HopByHopHeaderRouterAlertPG* hopByHop;
	hopByHop = (HopByHopHeaderRouterAlertPG*)&(packet[sizeof(IP6HeaderPG)]);
	hopByHop->NextHeader = 0x3A;	// ICMPv6
	hopByHop->HdrExtLen  = 0;
	hopByHop->Data[0]    = 0x05;	// Router Alert option
	hopByHop->Data[1]    = 0x02;
	hopByHop->Data[2]    = 0x00;
	hopByHop->Data[3]    = 0x00;
	hopByHop->Data[4]    = 0x01;	// PadN option
	hopByHop->Data[5]    = 0x00;

ICMP6MLD* mldh;

	mldh = (ICMP6MLD*)&(packet[sizeof(IP6HeaderPG) + sizeof(HopByHopHeaderRouterAlertPG)]);

	memset(mldh, 0, sizeof(ICMP6MLD));
	mldh->Type = 131;
	mldh->Code = 0;
	mldh->MaximumResponseDelay = htons(maximumResponseDelay);
	mldh->Reserved = 0;
	memcpy(mldh->McastAddr, mcastAddr, sizeof(in_addr6));

	nextHeader = 0x3A;	// ICMPv6
	dwLen      = sizeof(ICMP6MLD);

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte,   16);
	memcpy(pseudoHeader.Destination, mcastAddr->u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	// for computing the checksum
	mldh->Checksum = 0;
	// then the new value
	mldh->Checksum = ComputeCheckSum((unsigned short*)mldh, dwLen);

	packSize = ttsize;
	return true;
}

//---------------------------------------------------------------------------

BYTE* PacketBuilder6::GetPacket(void)
{
	return packet;
}

DWORD PacketBuilder6::GetPacketSize(void)
{
	return packSize;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketEmptyIPv6Proto(
		in_addr6* srcAddr,			///< source address
		in_addr6* dstAddr,			///< destination address
		BYTE      bTrafficClass,	///< trafic class
		DWORD     dwFlowLabel,		///< flow label
		BYTE      bHopLimit,		///< hop limit
		BYTE      bNextHeader		///< next header
	)
{
DWORD ttsize;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;

	ttsize  = sizeof(IP6HeaderPG);

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}
	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	if(!BuildIPHeader(packet, srcAddr, dstAddr, bTrafficClass, dwFlowLabel, 0, bNextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

	packSize = ttsize;
	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketTCP(
		in_addr6* srcAddr,		///< source address
		in_addr6* dstAddr,		///< destination address

		BYTE  bTrafficClass,	///< trafic class
		DWORD dwFlowLabel,		///< flow label
		BYTE  bHopLimit,		///< hop limit

		WORD wSP, WORD wDP,		///< source and destination port

		DWORD dwSeq,			///< sequence
		DWORD dwAck,			///< acknowledge
		BYTE bFlags,			///< tcp flags
		WORD wWindow,			///< window
		WORD wUrgent,			///< urgent pointer

		BYTE* bpOpt,			///< options
		DWORD dwOptLen,			///< length of options

		BYTE* bpData,			///< data
		DWORD dwDataLen			///< length of data
	)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;

	if(bpOpt==NULL && dwOptLen!=0) return false;
	if(dwOptLen&0x00000003) return false; // should be an integral number of 32 bit words

	if(bpData==NULL && dwDataLen!=0) return false;

	nextHeader = IPPROTO_TCP;

	ttsize  = sizeof(IP6HeaderPG);
	ttsize += sizeof(TCPHeaderPG);
	ttsize += dwOptLen;
	ttsize += dwDataLen;

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}
	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen  = sizeof(TCPHeaderPG);
	dwLen += dwOptLen;
	dwLen += dwDataLen;

	if(!BuildIPHeader(packet, srcAddr, dstAddr, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

TCPHeaderPG* tcph;

	tcph = (TCPHeaderPG*)&(packet[sizeof(IP6HeaderPG)]);

	tcph->SourcePort      = htons(wSP);
	tcph->DestinationPort = htons(wDP);

	tcph->Seq = dwSeq;
	tcph->Ack = dwAck;

	dwLen = sizeof(TCPHeaderPG) + dwOptLen;
	dwLen = dwLen>>2;
	dwLen = dwLen<<4;
	dwLen = dwLen & 0x000000FF;
	tcph->Offset = (BYTE)dwLen;

	tcph->Flags = bFlags;

	tcph->Window = htons(wWindow);

	tcph->Checksum = 0x0000;

	tcph->UrgentPointer = wUrgent;

BYTE* bbb;
	if(bpOpt!=NULL){
		bbb = &(packet[sizeof(IP6HeaderPG) + sizeof(TCPHeaderPG)]);
		memcpy(bbb, bpOpt, dwOptLen);
	}

	if(bpData!=NULL){
		bbb = &(packet[sizeof(IP6HeaderPG) + sizeof(TCPHeaderPG) + dwOptLen]);
		memcpy(bbb, bpData, dwDataLen);
	}

	dwLen  = sizeof(TCPHeaderPG);
	dwLen += dwOptLen;
	dwLen += dwDataLen;

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte, 16);
	memcpy(pseudoHeader.Destination, dstAddr->u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	tcph->Checksum = ComputeCheckSum((unsigned short*)tcph, dwLen);

	packSize = ttsize;
	return true;
}

//---------------------------------------------------------------------------

bool PacketBuilder6::BuildPacketUDP(
		in_addr6* srcAddr,		///< source address
		in_addr6* dstAddr,		///< destination address

		BYTE  bTrafficClass,	///< trafic class
		DWORD dwFlowLabel,		///< flow label
		BYTE  bHopLimit,		///< hop limit

		WORD wSP, WORD wDP,		///< source and destination port

		BYTE* bpData,			///< data
		DWORD dwDataLen			///< length of data
	)
{
DWORD ttsize;
DWORD dwLen;
BYTE  nextHeader;

	packSize = 0;

	if(srcAddr==NULL) return false;
	if(dstAddr==NULL) return false;

	if(bpData==NULL && dwDataLen!=0) return false;

	nextHeader = IPPROTO_UDP;

	ttsize  = sizeof(IP6HeaderPG);
	ttsize += sizeof(UDPHeaderPG);
	ttsize += dwDataLen;

	if(packet!=NULL){
		delete[] packet;
		packet = NULL;
	}
	try{
		packet = new BYTE[ttsize];
	}
	catch(...){
		packet = NULL;
	}
	if(packet==NULL) return false;

	dwLen  = sizeof(UDPHeaderPG);
	dwLen += dwDataLen;

	if(!BuildIPHeader(packet, srcAddr, dstAddr, bTrafficClass, dwFlowLabel, (WORD)dwLen, nextHeader, bHopLimit)){
		delete[] packet;
		packet = NULL;
		return false;
	}

UDPHeaderPG* udph;

	udph = (UDPHeaderPG*)&(packet[sizeof(IP6HeaderPG)]);

	udph->SourcePort      = htons(wSP);
	udph->DestinationPort = htons(wDP);

	dwLen = sizeof(UDPHeaderPG) + dwDataLen;
	dwLen = dwLen & 0x000000FF;
	udph->Length = htons((WORD)dwLen);

	udph->Checksum = 0x0000;

BYTE* bbb;

	if(bpData!=NULL){
		bbb = &(packet[sizeof(IP6HeaderPG) + sizeof(UDPHeaderPG)]);
		memcpy(bbb, bpData, dwDataLen);
	}

	dwLen  = sizeof(UDPHeaderPG);
	dwLen += dwDataLen;

	memcpy(pseudoHeader.Source,      srcAddr->u.Byte, 16);
	memcpy(pseudoHeader.Destination, dstAddr->u.Byte, 16);
	pseudoHeader.PacketLength = htonl(dwLen);
	pseudoHeader.Zero[0] = 0;
	pseudoHeader.Zero[1] = 0;
	pseudoHeader.Zero[2] = 0;
	pseudoHeader.NextHeader = nextHeader;

	udph->Checksum = ComputeCheckSum((unsigned short*)udph, dwLen);

	if(udph->Checksum==0x0000)
		udph->Checksum = 0xFFFF;	// RFC 2460 :: 8.1

	packSize = ttsize;
	return true;
}

//---------------------------------------------------------------------------
